/**
 * 
 */
package dk.itu.mobile.server;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

import dk.itu.mobile.util.TaskListSerializer;
import dk.itu.mobile.vo.Task;
import dk.itu.mobile.vo.TaskList;
import dk.itu.mobile.vo.User;

/**
 * @author niebje
 * 
 */
public class TcpServer implements Runnable {

	private int port;
	private Socket socket;
	private DataOutputStream dataOutputStream;
	private DataInputStream dataInputStream;
	private ObjectInputStream objectInputStream;
	private ObjectOutputStream objectOutputStream;
	private String currentRequest = "";
	private TaskList tasklist;
	private String path;

	public TcpServer(int port) {
		this.port = port;
		path = "data/task-manager-xml.xml";
	}

	@Override
	public void run() {
		startServer();
		Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO,
				"Server Closed");
	}

	public void startServer() {
		try {
			ServerSocket serverSocket = new ServerSocket(port);
			socket = serverSocket.accept();
			System.out.println("Server startet at port: " + port);
			loadTaskList();
			listen();

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void loadTaskList() {
		tasklist = new TaskList();
		
		tasklist = TaskListSerializer.load(path, TaskList.class);
	}

	private void listen() {
		try {
			dataInputStream = new DataInputStream(socket.getInputStream());
			// Blocking listen
			System.out.println("Server awaiting input on port: " + port);
			String message = dataInputStream.readUTF();
			Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.INFO,
					"Message from client:" + message);
			handleRequest(message);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private void waitForObject() {
		try {
			objectInputStream = new ObjectInputStream(socket.getInputStream());
			Object object = objectInputStream.readObject();
			Boolean result = handleObjectRequest(object);
			if ( result.equals(true)) {
				respond(currentRequest+" operation on " + object.getClass().getName()+" completed sucessfully" );
			} else {
				respond(currentRequest+" operation on " + object.getClass().getName()+" did not go well. The object was not found in the list" );
			}
			currentRequest ="";
			listen();	
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private boolean handleObjectRequest(Object object) {
		if ( currentRequest.equals("POST")) {
			if (object instanceof Task) {
				Task task = (Task) object;
				if ( !tasklist.tasks.contains(task)) {
					tasklist.tasks.add(task);
					TaskListSerializer.save(tasklist, path);
					return true;
				}
				
			} else if ( object instanceof User ) {
				User user = (User) object;
				if ( !tasklist.users.contains(user)) {
					tasklist.users.add(user);
					TaskListSerializer.save(tasklist, path);
					return true;
				}
			}
			
		}else if (currentRequest.equals("PUT")) {
			if (object instanceof Task) {
				Task task = (Task) object;
				if ( tasklist.tasks.contains(task)) {
					tasklist.updateTask(task);
					TaskListSerializer.save(tasklist, path);
					return true;
				}
				
			} else if ( object instanceof User ) {
				User user = (User) object;
				if ( tasklist.users.contains(user)) {
					tasklist.updateUser(user);
					TaskListSerializer.save(tasklist, path);
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 
	 * @param message
	 *            a string, current implementation supports GET
	 */
	private void handleRequest(String message) {
		if (currentRequest == "") {
			if (message.equals("GET") || message.equals("PUT")
					|| message.equals("POST") || message.equals("DELETE")) {
				currentRequest = message;
				respond(message);

				if (message.equals("GET") || message.equals("DELETE")) { //Operations only by ID
					listen();
				} else if (message.equals("PUT") || message.equals("POST")) {
					waitForObject();
				}
			}
		} else {
			if (currentRequest.equals("GET")) {
				String[] requestParts = message.split(",");
				Object object = buildGetResponse(requestParts);
				if ( object != null ) {
					if (object instanceof String) {
						respond((String) object);
					}else {
						objectRespond(object);
					}	
				} else {
					respond("The object with the id/name of "+ requestParts[1] + " was not found");
				}
				
				currentRequest = "";
			} else if (currentRequest.equals("DELETE")) {
				String[] requestParts = message.split(",");
				objectRespond(handleDeleteRequest(requestParts));
			}

			currentRequest = ""; // done processing
			listen();
		}

	}
	
	private String handleDeleteRequest(String[] requestParts) {
		if (requestParts[0].equals("USER")) {
			User user = tasklist.getUserByName(requestParts[1]);
			if (user != null){
				tasklist.users.remove(user);
				TaskListSerializer.save(tasklist, path);
				return "Successfully delete the user with username: "+requestParts[1];
			}
		}else if (requestParts[0].equals("TASK")) {
			Task task = tasklist.getTaskById(requestParts[1]);
			if (task != null) {
				tasklist.tasks.remove(task);
				TaskListSerializer.save(tasklist, path);
				return "Successfully deleted the task with id: "+requestParts[1];
			}
		}
		return "the " + requestParts[0].toLowerCase() + " with the id/Name: "
		+ requestParts[1].toLowerCase() + " was not found";
	}

	private Object buildGetResponse(String[] requestParts) {
		if (requestParts[0].equals("USER")) {
			return tasklist.getUserByName(requestParts[1]);
		} else if (requestParts[0].equals("TASK")) {
			return tasklist.getTaskById(requestParts[1]);
		}
		return "the " + requestParts[0].toLowerCase() + " with the id/Name: "
				+ requestParts[1].toLowerCase() + " was not found";
	}

	private void respond(String message) {
		try {
			dataOutputStream = new DataOutputStream(socket.getOutputStream());
			dataOutputStream.writeUTF(message);
			dataOutputStream.flush();
			// dataOutputStream.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private void objectRespond(Object object) {
		try {
			objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
			objectOutputStream.writeObject(object);
			objectOutputStream.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void close() {
		try {
			socket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
